package ch.fusun.baron.player;

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import ch.fusun.baron.core.datastructures.MirrorMap;
import ch.fusun.baron.core.datastructures.Pair;
import ch.fusun.baron.data.AbstractDataProvider;
import ch.fusun.baron.data.DataUpdate;
import ch.fusun.baron.player.api.MarriageService;
import ch.fusun.baron.player.api.MarriageUpdate;

/**
 * Adds functionality for marriages and marriage status
 */
public class MarriageServiceImpl extends AbstractDataProvider implements
        MarriageService {
    private final MirrorMap<Player> marriage = new MirrorMap<Player>();
    /**
     * Key proposes to value
     */
    private final Map<Player, Player> marriageProposals = new HashMap<Player, Player>();

    /**
     * Kryo constructor
     */
    public MarriageServiceImpl() {
    }

    @Override
    public DataUpdate createFullUpdate() {
        return new MarriageUpdate(marriage, marriageProposals);
    }

    @Override
    public boolean isMarried(Player player) {
        return getSpouse(player) != null;
    }

    @Override
    public Player getSpouse(Player player) {
        return marriage.get(player);
    }

    @Override
    public void proposeMarriage(Player proposer, Player proposedTo) {
        if (!proposer.getGender().equals(proposedTo)) {
            synchronized (MarriageServiceImpl.class) {
                marriageProposals.put(proposer, proposedTo);
            }
            updateAllListeners(new MarriageProposedDataUpdate(proposer,
                    proposedTo));
        }
    }

    @Override
    public void acceptMarriage(Player proposer, Player proposedTo) {
        if (proposedTo.equals(marriageProposals.get(proposer))) {
            synchronized (MarriageServiceImpl.class) {
                marriageProposals.remove(proposer);
                marriage.put(proposer, proposedTo);
            }
            updateAllListeners(new MarriageAcceptedDataUpdate(proposer,
                    proposedTo));
        }
    }

    @Override
    public void declineMarriage(Player proposer, Player proposedTo) {
        marriageProposals.remove(proposer);
        updateAllListeners(new MarriageDeclinedDataUpdate(proposer, proposedTo));
    }

    @Override
    public void setMarriageStatus(MirrorMap<Player> marriage,
            Map<Player, Player> marriageProposals) {
        this.marriage.clear();
        this.marriageProposals.clear();

        for (Pair<Player> entry : marriage.entrySet()) {
            this.marriage.put(entry);
        }
        for (Entry<Player, Player> entry : marriageProposals.entrySet()) {
            this.marriageProposals.put(entry.getKey(), entry.getValue());
        }
        updateAllListeners(new MarriageUpdate(marriage, marriageProposals));
    }

    @Override
    public Collection<Player> getAllMarriedMen() {
        return marriage.keySet();
    }

    @Override
    public void marry(Player male, Player female) {
        marriage.put(male, female);
        updateAllListeners(new MarryDataUpdate(male, female));
    }

    @Override
    public void divorce(Player person) {
        marriage.remove(person);
        updateAllListeners(new DivorceDataUpdate(person));
    }

    @Override
    public List<Entry<Player, Player>> getProposals() {
        return new LinkedList<Entry<Player, Player>>(
                marriageProposals.entrySet());
    }

}
